package com.taldh.algorithm.array;

import org.junit.Assert;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.TreeSet;
import java.util.stream.Collectors;

public class RemoveDuplicatesSolution {
    /**
     * 给定一个排序数组，你需要在原地删除重复出现的元素，使得每个元素只出现一次，返回移除后数组的新长度。
     *
     * 不要使用额外的数组空间，你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
     *
     * 示例 1:
     *
     * 给定数组 nums = [1,1,2],
     *
     * 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
     *
     * 你不需要考虑数组中超出新长度后面的元素。
     * 示例 2:
     *
     * 给定 nums = [0,0,1,1,1,2,2,3,3,4],
     *
     * 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
     *
     * 你不需要考虑数组中超出新长度后面的元素。
     * 说明:
     *
     * 为什么返回数值是整数，但输出的答案是数组呢?
     *
     * 请注意，输入数组是以“引用”方式传递的，这意味着在函数里修改输入数组对于调用者是可见的。
     *
     * 你可以想象内部操作如下:
     *
     * // nums 是以“引用”方式传递的。也就是说，不对实参做任何拷贝
     * int len = removeDuplicates(nums);
     *
     * // 在函数里修改输入数组对于调用者是可见的。
     * // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
     * for (int i = 0; i < len; i++) {
     *     print(nums[i]);
     * }
     *
     * 来源：力扣（LeetCode）
     * 链接：https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array
     * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
     */
    //两年前的算法
//    public int removeDuplicates(int[] nums) {
//        List<Integer> collect = Arrays.stream(nums).sorted().boxed().collect(Collectors.toList());
//        TreeSet<Integer> integerSet = new TreeSet<>(collect);
//        int i = 0;
//        for (Integer num: integerSet) {
//            nums[i] = num;
//            i ++;
//        }
//        return integerSet.size();
//    }
//
//    public int removeDuplicates(int[] nums) {
//        int len = nums.length;
//        int lastNonDuplicateNum = 0;
//        int lastNonDuplicateIndex = 1;
//        for (int i = 0; i < len; i++) {
//            //1. get first non duplicate num
//            boolean secondExistElement = isSecondExistElement(nums, i);
//            if (!secondExistElement) {
//                lastNonDuplicateNum++;
//                continue;
//            }
//            int firstNonDuplicateNum = getFirstNonDuplicateNum(nums, Math.max(lastNonDuplicateIndex, i));
//            if (firstNonDuplicateNum == -1) {
//                continue;
//            }
//            lastNonDuplicateIndex = firstNonDuplicateNum;
//            nums[lastNonDuplicateNum] = nums[firstNonDuplicateNum];
//            lastNonDuplicateNum++;
//        }
//        return lastNonDuplicateNum;
//    }
//
//    private boolean isSecondExistElement(int[] nums, int i) {
//        for (int j = 0; j < i; j++) {
//            if (nums[j] == nums[i]) {
//                return true;
//            }
//        }
//        return false;
//    }
//
//    private int getFirstNonDuplicateNum(int[] nums, int i) {
//        int ret = -1;
//        int len = nums.length;
//        for (int j = i+1; j < len; j++) {
//            if (nums[j] != nums[i]) {
//                ret =  j;
//                break;
//            }
//        }
//        return ret;
//    }

//    public int removeDuplicates(int[] nums) {
//        int len = nums.length;
//        int i = 0;
//        for (int j = 1; j < len; j++) {
//            if (nums[j] != nums[i]) {
//                i ++;
//                nums[i] = nums[j];
//            }
//        }
//        return i + 1;
//    }
//
//    @Test
//    public void test() {
//        Assert.assertEquals(2, removeDuplicates(new int[]{1,1,2}));
//    }
//
//    @Test
//    public void test1() {
//        Assert.assertEquals(5, removeDuplicates(new int[]{0,0,1,1,1,2,2,3,3,4}));
//    }
//
//    @Test
//    public void test2() {
//        Assert.assertEquals(1, removeDuplicates(new int[]{1}));
//    }
//
//    @Test
//    public void test3() {
//        Assert.assertEquals(3, removeDuplicates(new int[]{1,2,3,3}));
//    }

    /**
     *给定一个排序数组，你需要在原地删除重复出现的元素，使得每个元素最多出现两次，返回移除后数组的新长度。
     *
     * 不要使用额外的数组空间，你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
     *
     * 示例 1:
     *
     * 给定 nums = [1,1,1,2,2,3],
     *
     * 函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。
     *
     * 你不需要考虑数组中超出新长度后面的元素。
     * 示例 2:
     *
     * 给定 nums = [0,0,1,1,1,1,2,3,3],
     *
     * 函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。
     *
     * 你不需要考虑数组中超出新长度后面的元素。
     * 说明:
     *
     * 为什么返回数值是整数，但输出的答案是数组呢?
     *
     * 请注意，输入数组是以“引用”方式传递的，这意味着在函数里修改输入数组对于调用者是可见的。
     *
     * 你可以想象内部操作如下:
     *
     * // nums 是以“引用”方式传递的。也就是说，不对实参做任何拷贝
     * int len = removeDuplicates(nums);
     *
     * // 在函数里修改输入数组对于调用者是可见的。
     * // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
     * for (int i = 0; i < len; i++) {
     *     print(nums[i]);
     * }
     *
     * 来源：力扣（LeetCode）
     * 链接：https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array-ii
     * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
     */

    public int removeDuplicates(int[] nums) {
        int len = nums.length;
        if (len < 2) {
            return len;
        }

        int i = 1;
        int j = 1;
        int count = 0;
        while (j < len) {
            if (nums[j] == nums[j-1]) {
                count++;
            } else {
                count = 0;
            }
            if (count < 2) {
                nums[i] = nums[j];
                i++;
            }
            j ++;
        }
        return i;
    }

    @Test
    public void test() {
        Assert.assertEquals(7, removeDuplicates(new int[]{0,0,1,1,1,1,2,3,3}));
    }
    @Test
    public void test2() {
        Assert.assertEquals(5, removeDuplicates(new int[]{1,1,1,2,2,3}));
    }
    @Test
    public void test3() {
        Assert.assertEquals(2, removeDuplicates(new int[]{1,1,1}));
    }


}